<?php
/**
 * 自定义用户提供者
 * 将缓存用户信息一小时，可减少请求查询用户表信息
 * CustomEloquentUserProvider::refresh(uid) 将清除缓存信息
 *
 * @date    2019-08-13 19:41:48
 * @version $Id$
 */

namespace App\Commons\Libs\JWTAuth;

use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;

/**
 * 账号用户关系模型如：
 * Account<---N:1-->User
 * Account和User都有uid字段
 * 登录时调用retrieveByCredentials查询Account，并且返回的是User
 * 请求接口时通过uid调用retrieveById查询用户表
 * 此时可从User表查询
 * 这样auth()->user()获取的就是user表
 */
class CustomEloquentUserProvider extends EloquentUserProvider
{
    // 缓存Account账号信息时间
    const CACHE_MINUTE = 3600;

    protected $fields = [];
    protected $auth_model;

    /**
     * Create a new database user provider.
     *
     * @param  \Illuminate\Contracts\Hashing\Hasher  $hasher
     * @param  string  $model
     * @return void
     */
    public function __construct(HasherContract $hasher, $model, $auth_model, $fields = [])
    {
        parent::__construct($hasher, $model);
        $this->fields = $fields;
        $this->auth_model = $auth_model;
    }
    /**
     * 缓存数据
     * @param  string   $key      [description]
     * @param  callable $callback [description]
     * @return [type]             [description]
     */
    private function cache(string $key, callable $callback)
    {
        return cache()->tags(['auth'])->remember(class_basename(__CLASS__). ':'. $key, static::CACHE_MINUTE, $callback);
    }

    /**
     * 刷新登录缓存
     * @param  string $key [description]
     * @return [type]      [description]
     */
    public static function refresh(string $key)
    {
        return cache()->tags(['auth'])->forget(class_basename(__CLASS__). ':'. $key);
    }

    private function has(string $key): bool
    {
        return cache()->tags(['auth'])->has(class_basename(__CLASS__). ':'. $key);
    }
    /**
     * Retrieve a user by their unique identifier.
     *
     * @param  mixed  $identifier
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveById($identifier)
    {
        // $is_cached = $this->has($identifier);

        $user = $this->cache(
            $identifier,
            function () use ($identifier) {
                $model = $this->createModel();
                $user = $this->newModelQuery($model)
                    ->where($model->getAuthIdentifierName(), $identifier)
                    ->select($this->fields)
                    ->first();
                throw_empty($user, 0xf00012);
                $user->setHidden(['gender_text', 'password']);
                return $user->toArray();
            }
        );
        $model = $this->createCacheModel($user);

        return $model;
    }

    /**
     * Create a new instance of the model.
     *
     * @return \Illuminate\Database\Eloquent\Model
     */
    protected function createCacheModel($attributes)
    {
        $model = $this->createModel();
        $model->fill($attributes);
        $model->id = $attributes['id'];
        $model->exists = true;

        return $model;
    }

    /**
     * Retrieve a user by their unique identifier and "remember me" token.
     *
     * @param  mixed  $identifier
     * @param  string  $token
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByToken($identifier, $token)
    {
        // $is_cached = $this->has($identifier);
        throw_e('暂不支持记住我的');
        $user = $this->cache(
            $identifier,
            function () use ($identifier, $token) {
                $acc = parent::retrieveByToken($identifier, $token);
                $user = $acc->user()->select($this->fields)->first();
                throw_empty($user, 0xf00012);
                $user->setHidden(['gender_text', 'password']);
                return $user->toArray();
            }
        );

        $model = $this->createCacheModel($user);

        return $model;
    }

    public function retrieveByCredentials(array $credentials)
    {
        // 切换到验证模型，即Account表
        $bak_model = $this->model;
        $this->model = $this->auth_model;
        $model = throw_empty(parent::retrieveByCredentials($credentials), 0xf00012);

        // 切换回来
        $this->model = $bak_model;
        static::refresh($model->uid);
        $model->user = $this->retrieveById($model->uid);

        throw_on(!empty($model->user->disabled_at), 0xf00242);
        // 提供user模型验证密码功能
        $model->user->password = $model->password;
        $model->user->setHidden(['gender_text', 'password']);

        return $model->user;
    }
}
